<?php

class Crud_Form extends Zend_Form
{
	/**
	 * Table for building form.
	 * 
	 * @var Crud_Section
	 */
	protected $_section;
	
	/**
	 * Table row for current form.
	 * Default values set from it.
	 * 
	 * @var Crud_Section_Row
	 */
	protected $_row;
	
	/**
	 * Array of crud form elements.
	 * 
	 * @var array
	 */
	protected $_crudElements = array();
	
    /**
     * Store Crud_Section in form.
     * Registers form view helper as decorator
     *
     * @param Crud_Section $section
     * @return void
     */
    public function __construct(Crud_Section $section, Crud_Section_Row $row = null)
    {
    	$this->setSection($section);
    	if ($row) {
    		$this->setRow($row);
    	}
    	parent::__construct();
    	
    	$this->addPrefixPath('Crud_Form_Decorator', 'Crud/Form/Decorator', 'decorator');
    	$this->addElementPrefixPath('Crud_Form_Decorator', 'Crud/Form/Decorator', 'decorator');
    	$this->addElementPrefixPath('Validate', 'Validate/', 'validate');
        $this->addElementPrefixPath('Filter', 'Filter/', 'filter');
    }
	
	
	/**
	 * Adding element to form.
	 * 
	 * @return void
	 */
	public function init()
	{	
		// Adding Error decorator
		$errorDecorator = new Crud_Form_Decorator_Errors(array(
			'placement' => Zend_Form_Decorator_Abstract::PREPEND
		));
						
		$this->addDecorators(array('FormElements', 'HtmlTag', 'Form', $errorDecorator));
		
		// Create elements for each table columns
		$section = $this->getSection();
		$columns = $section->getColumns();
		foreach ($columns as $column) {
			if (!$column->getShowInItem()) {
				continue;
			}
			
			$this->_addElementByColumn($column);
		}
		
		// Set custom viewscript decorator
		if ($section->getViewScript()) {
			$decoratorScript = new Zend_Form_Decorator_ViewScript();
			$decoratorScript->setViewScript($section->getViewScript());
		    $this->setDecorators(array(
		        new Zend_Form_Decorator_PrepareElements(),
		    	$decoratorScript,
		    ));
		}
		
		// Set action for form and submit button
		$this->setMethod(Zend_Form::METHOD_POST);
			
		$submit = new Zend_Form_Element_Submit('submit');
		$submit->setOrder(100);
		$submit->setLabel('Сохранить');
		$this->addElement($submit);
		
		$exit = new Zend_Form_Element_Submit('crud_exit');
		$exit->setOrder(101);
		$exit->setLabel('Выйти');
		$this->addElement($exit);
	}
	
	/**
	 * Append element to form by Crud_Section_Column
	 * 
	 * @param Crud_Section_Column
	 * @return void
	 */
	protected function _addElementByColumn(Crud_Section_Column $column)
	{
		// Create element and append it to form
		$cElement = Crud_Form_Element::factory($column);
		if (!$this->getRow() && $column->isMany2many()) {
			return;
		}
		$this->addElement($cElement);
		
		$element = $cElement->getElement();
		// Hack, required opearator_id in call_center
		if ($element->getName() == 'operator_id' && $this->getSection()->getKey() == 'orders') {
			$element->setRequired(true);
		}
		
		// Set name && description for element
		$element->setLabel($column->getName());
		$element->setDescription($column->getDescription());
		
		// Set default value
		$defaultValue = $cElement->getRowValue($this->getRow());
		$element->setValue($defaultValue);
	}
	
	/**
	 * @return Crud_Section
	 */
	public function getSection()
	{
		return $this->_section;
	}
	
	/**
	 * Set Crud_Section for current form.
	 * 
	 * @return void
	 */
	public function setSection(Crud_Section $section)
	{
		$this->_section = $section;
	}
	
	/**
	 * @return Crud_Section_Row
	 */
	public function getRow()
	{
		return $this->_row;
	}
	
	/**
	 * Set Crud_Section_Row for current form.
	 * 
	 * @return void
	 */
	public function setRow(Crud_Section_Row $row)
	{
		$this->_row = $row;
	}
	
    /**
     * Extended method can add Crud_Form_Element.
     * ------
     * Add a new element
     *
     * $element may be either a string element type, or an object of type
     * Zend_Form_Element. If a string element type is provided, $name must be
     * provided, and $options may be optionally provided for configuring the
     * element.
     *
     * If a Zend_Form_Element is provided, $name may be optionally provided,
     * and any provided $options will be ignored.
     *
     * @param  string|Zend_Form_Element $element
     * @param  string $name
     * @param  array|Zend_Config $options
     * @return Zend_Form
     */
    public function addElement($element, $name = null, $options = null)
    {
    	if ($element instanceof Crud_Form_Element) {
    		$name = $element->getElement()->getName();
    		$this->_crudElements[$name] = $element;
    		$element = $element->getElement();
    		if (!$element) {
    			throw new Crud_Exception('Element is not valid Zend_Form_Element');
    		}
    	}
    	
    	parent::addElement($element, $name, $options);
    }

    /**
     * Return crud form element by his name.
     * 
     * @param string $name 
     * @return Crud_Form_Element_Column
     */
    public function getCrudElement($key)
    {
    	if (!isset($this->_crudElements[$key])) {
    		throw new Crud_Exception('Element with name `$key` does not exists in the form');
    	}
    	
    	return $this->_crudElements[$key];
    }
    
    /**
     * Return crud form elements.
     * 
     * @return array
     */
    public function getCrudElements()
    {
    	return $this->_crudElements;
    }
}
